package com.yenlien.traceyun.plant.dock_device.kebai;

import com.alibaba.fastjson.JSONObject;
import com.yenlien.traceyun.business.dao.BaseSoilDao;
import com.yenlien.traceyun.business.entity.BaseSoilEquipment;
import com.yenlien.traceyun.business.entity.Equipment;
import com.yenlien.traceyun.business.service.EquipmentService;
import com.yenlien.traceyun.common.utils.HttpUtil;
import com.yenlien.traceyun.plant.dao.DevAlarmDao;
import com.yenlien.traceyun.plant.dao.DevThresholdDao;
import com.yenlien.traceyun.plant.dao.KeBaiDao;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.http.Consts;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

import static com.yenlien.traceyun.plant.dock_device.DevState.EQU_MANUFACTURER_KB;

/**
 * @author jialong
 * @date 2021/03/31
 * @description: 定时获取科百平台的气象数据  彭州岳池茂县目前使用的设备都是新风向，所以没有设置userName和password,所以可以注释掉
 *          安装了 科百 的气象设备 该定时任务才需要执行
 **/

@Component
@Slf4j
public class KeBaiGetData {

    @Value("${keBai.loginUrl}")
    private String loginUrl;

    @Value("${keBai.getAllDataByUserUrl}")
    private String getAllDataByUserUrl;

    @Value("${keBai.userName}")
    public String userName;

    @Value("${keBai.password}")
    public String password;

    // 保存登陆后的Cookie, 每次请求都要带上
    private static String cookieValue = null;

    @Autowired
    private KeBaiDao keBaiDao;

    @Autowired
    private EquipmentService equipmentService;


    @Autowired
    private BaseSoilDao baseSoilDao;

    @Autowired
    private DevThresholdDao devThresholdDao;

    @Autowired
    private DevAlarmDao devAlarmDao;

    // 6个小时执行一次
    public final static int fixedRate = 1000 * 3600 * 6;

//    @Scheduled(fixedRate = fixedRate, initialDelay = fixedRate)
//    @Scheduled(fixedRate = fixedRate)
    public void getData() {

        // 重置Cookie, 避免Cookie过期导致无法访问
        cookieValue = null;

        List<KeBaiData> allDataByUser = getAllDataByUser(userName, password);
        if (allDataByUser != null && allDataByUser.size() > 0) {
            // 跟据厂商查询以绑定的设备
            List<Equipment> equipmentList = equipmentService.findByManufacturer(EQU_MANUFACTURER_KB);
            if (equipmentList == null || equipmentList.size() == 0) {
                log.error("t_equipment中没有设备: {}", equipmentList);
                return;
            }
            List<String> equipmentIds = equipmentList.stream().map(Equipment::getId).collect(Collectors.toList());
            List<BaseSoilEquipment> equipmentBaseSoilList = equipmentService.getEquipmentBaseSoil(equipmentIds);    //获取所有设备与地块的对应关系,
            if (equipmentBaseSoilList == null || equipmentBaseSoilList.size() == 0) {
                log.error("t_base_soil_equipment中没有设备与地块的对应关系: {}", equipmentBaseSoilList);
                return;
            }
            List<KeBaiData> dataList = bind(equipmentList, allDataByUser, equipmentBaseSoilList);


            checkAllIsSuccess(keBaiDao.saveAll(dataList));
        } else {
            log.error("请求获取当前用户接口的数据处理后为0条, 接口路径: {}", getAllDataByUserUrl);
        }
    }

    /**
     * 根据设备信息和地块ID进行数据绑定  过自己平台的设备信息表里的 serial 字段与对方平台设备进行关联
     *
     * @param equipmentList
     * @param allDataByUser
     * @param equipmentBaseSoilList
     * @return
     */
    public List<KeBaiData> bind(List<Equipment> equipmentList, List<KeBaiData> allDataByUser, List<BaseSoilEquipment> equipmentBaseSoilList) {
        for (Equipment equipment : equipmentList) {
            String serial = equipment.getSerial();
            if (StringUtils.isBlank(serial)) {
                continue;
            }
            if (CollectionUtils.isEmpty(allDataByUser)) {
                continue;
            }
            for (KeBaiData keBai : allDataByUser) {
                if (serial.equals(keBai.getNodecode())) {
                    keBai.setCompanyId(equipment.getCompanyId());
                    keBai.setEquipmentId(equipment.getId());
                    keBai.setEquipmentName(equipment.getName());
                    keBai.setCreateTime(new Date());
                    if (equipmentBaseSoilList != null || equipmentBaseSoilList.size() > 0) {
                        for (BaseSoilEquipment baseSoilEquipment : equipmentBaseSoilList) {
                            if (equipment.getId().equals(baseSoilEquipment.getEquipmentId())) {
                                keBai.setBaseSoilId(baseSoilEquipment.getBaseSoilId());
                            }
                        }
                    }
                }
            }
        }
        // 只插入在我们平台已绑定的设备
        return allDataByUser.stream().filter(x -> StringUtils.isNotBlank(x.getBaseSoilId())).collect(Collectors.toList());
    }


    /**
     * 根据用户名和账号登录科百平台
     *
     * @param userName
     * @param password
     * @return
     */
    public boolean login(String userName, String password) {
        Map<String, Object> params = new HashMap<>();
        params.put("username", userName);
        params.put("password", password);
        JSONObject result = JSONObject.parseObject(loginPost(loginUrl, JSONObject.toJSONString(params)));
        if (result == null) {
            log.error("解析科百登录接口的返回值为null， 接口路径: {}", loginUrl);
            return false;
        }
        if (Integer.parseInt(result.get("status").toString()) == 200 && "登陆成功！".equals(result.get("msg"))) {
            return true;
        } else {
            log.error("科百登录失败，错误信息: {}, 用户名和密码: {}", result.get("msg"), userName + " , " + password);
            return false;
        }
    }


    /**
     * 根据用户名获取所有传感器采集的数据
     *
     * @return
     */
    public List<KeBaiData> getAllDataByUser(String userName, String password) {
        JSONObject jsonObject = JSONObject.parseObject(get(getAllDataByUserUrl, null, userName, password));
        if (jsonObject == null) {
            log.error("解析科百用户名获取所有设备数据接口的返回值为null， 接口路径: {}", getAllDataByUserUrl);
            return new ArrayList<>();
        }
        if (Integer.parseInt(jsonObject.get("code").toString()) == 200 && "success".equals(jsonObject.get("msg"))) {
            // 部分传感器只有一份数据, 还有一部分传感器有12份数据(每份数据间隔5分钟), 所以要清除重复的数据，留最近的一条
            return JSONObject.parseArray(jsonObject.getJSONArray("data").toJSONString(), KeBaiData.class)
                    .stream()
                    .sorted(Comparator.comparing(KeBaiData::getThattime).reversed())    //按日期降序排,再配合sensorcode去重,即可保留最近的一条数据,无误
                    .collect(Collectors.collectingAndThen(
                            Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(KeBaiData::getSensorcode))), ArrayList::new)
                    );
        } else {
            log.error("请求科百据用户名获取所有设备数据接口失败， 错误信息: {}", jsonObject.get("msg"));
            return new ArrayList<>();
        }
    }





    /* --------------- 请求工具类 -------------- **/

    /**
     * 发送Http  Post请求，参数为json字符串, 把Cookie保存起来
     *
     * @param url     请求地址
     * @param jsonStr json字符串
     * @return
     */
    public String loginPost(String url, String jsonStr) {
        String result = "";
        StringEntity stringEntity = new StringEntity(jsonStr, Consts.UTF_8);
        stringEntity.setContentType("application/json");
        HttpPost httpPost = new HttpPost(url);
        httpPost.setEntity(stringEntity);
        try (CloseableHttpClient httpclient = HttpUtil.getBuilder().build(); CloseableHttpResponse httpResponse = httpclient.execute(httpPost);) {
            // 获取cookie, 调用登录接口后其他请求可直接辨别身份, 通过cookie
            Header[] allHeaders = httpResponse.getAllHeaders();
            for (Header header : allHeaders) {
                if ("Set-Cookie".equals(header.getName())) {
                    cookieValue = header.getValue();
                }
            }
            if (StringUtils.isBlank(cookieValue)) {
                log.error("在响应的allHeaders中未找到 Set-Cookie 属性");
            }
            HttpEntity httpEntity = httpResponse.getEntity();
            result = EntityUtils.toString(httpEntity);
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }


    /**
     * @param url
     * @param params
     * @return
     */
    public String get(String url, Map<String, String> params, String userName, String password) {
        CloseableHttpClient httpclient = HttpClients.createDefault();
        HttpGet httpGet = new HttpGet(url);
        CloseableHttpResponse response = null;
        // 如果没有值则再次请求登录拿到 Cookie,  对方可能设置了过期时间，导致一段时间后cookie失效
        login(userName, password);
        // 添加Cookie
        httpGet.addHeader("Cookie", cookieValue);
        try {
            if (params != null && params.size() > 0) {
                StringBuffer sbuf = new StringBuffer();
                for (Map.Entry<String, String> entry : params.entrySet()) {
                    sbuf.append("&").append(entry.getKey()).append("=").append(entry.getValue());
                }
                url = url + "?" + sbuf.substring(1);
            }
            response = httpclient.execute(httpGet);
            int code = response.getStatusLine().getStatusCode();
            log.info("url:{},status:{}", url, code);
            switch (code) {
                case 200:
                    HttpEntity entity = response.getEntity();
                    String result = EntityUtils.toString(entity);
                    EntityUtils.consume(entity);
                    return result;
            }
        } catch (IOException e) {
            log.error("httpUtil->get:{},errorInfo:{}", url, e.getMessage());
        } finally {
            try {
                if (response != null) {
                    response.close();
                }
                httpclient.close();
            } catch (IOException e) {
                log.error("httpUtil->get:{},errorInfo:{}", url, e.getMessage());
            }
        }
        return null;
    }

    /**
     * 根据有没有回填ID来判断是否插入成功
     */
    public void checkAllIsSuccess(List<KeBaiData> lists) {
        for (KeBaiData keBai : lists) {
            if (keBai.getId() == null) {
                log.error("插入新风向数据失败, 数据：{}", keBai);
            }
        }
    }
}
